package types

import (
	"fmt"
	"reflect"
)

type TypeInfo struct {
	// 可以看到的类型名称
	Name    string // 类型名称
	Package string // 所属包

	Type  reflect.Type // 原始类型
	IsPtr bool         // 原始类型是否指针

	IsSlice     bool // 原始类型是否数组
	IsMap       bool // 原始类型是否 Map 类型
	IsInterface bool // 原始类型是否接口
	IsFunc      bool // 原始类型是否函数

	IsTime               bool // 是否时间类型
	IsReferencePrimitive bool // 指向类型 是否原生类型
	IsReferenceStruct    bool // 指向类型 是否结构体

	ReferenceType reflect.Type // 指向类型，指针、数组、Map(value) 等指向的类型
	KeyType       reflect.Type // map 的 key 类型，只能支持原生类型，且不是指针

	IsReferenceTypePtr bool // 数组或map(vlaue)元素是否指针
}

func (t *TypeInfo) ID() string {
	return fmt.Sprintf("%s.%s", t.Package, t.Name)
}

// TypeName
func (t *TypeInfo) TypeName() string {
	if t.IsReferencePrimitive {
		kd := t.ReferenceType.Kind()
		if kd == reflect.Bool {
			return "bool"
		}
		if kd == reflect.Int ||
			kd == reflect.Int8 ||
			kd == reflect.Int16 ||
			kd == reflect.Int32 ||
			kd == reflect.Int64 ||
			kd == reflect.Uint ||
			kd == reflect.Uint8 ||
			kd == reflect.Uint16 ||
			kd == reflect.Uint32 ||
			kd == reflect.Uint64 {
			return "int"
		}
		if kd == reflect.Float32 || kd == reflect.Float64 {
			return "float"
		}
		if kd == reflect.String {
			return "string"
		}
	}
	return t.ReferenceType.Name()
}

// KeyTypeName
func (t *TypeInfo) KeyTypeName() string {
	var tp = t.KeyType
	if tp == nil {
		return ""
	}
	if tp.Kind() == reflect.Ptr {
		tp = tp.Elem()
	}

	if tp.PkgPath() == "" {
		kd := tp.Kind()
		if kd == reflect.Bool {
			return "bool"
		}
		if kd == reflect.Int ||
			kd == reflect.Int8 ||
			kd == reflect.Int16 ||
			kd == reflect.Int32 ||
			kd == reflect.Int64 ||
			kd == reflect.Uint ||
			kd == reflect.Uint8 ||
			kd == reflect.Uint16 ||
			kd == reflect.Uint32 ||
			kd == reflect.Uint64 {
			return "int"
		}
		if kd == reflect.Float32 || kd == reflect.Float64 {
			return "float"
		}
		if kd == reflect.String {
			return "string"
		}
	}
	return "Unsupport type"
}

var (
	// supportsPrimitiveTypes 支持的原生类型
	supportsPrimitiveTypes = map[reflect.Kind]struct{}{
		reflect.Bool: {},

		reflect.Int:    {},
		reflect.Int8:   {},
		reflect.Int16:  {},
		reflect.Int32:  {},
		reflect.Int64:  {},
		reflect.Uint:   {},
		reflect.Uint8:  {},
		reflect.Uint16: {},
		reflect.Uint32: {},
		reflect.Uint64: {},

		reflect.Float32: {},
		reflect.Float64: {},

		reflect.String: {},
	}
)

// ParseType 解析类型
func ParseType(typ reflect.Type, location string) (*TypeInfo, error) {
	//typName := typ.Name()
	//checkSupport(loc, typName, typ)

	kd := typ.Kind()
	info := &TypeInfo{
		Type:          typ,
		ReferenceType: typ,
		//Name:             typName,
		//Package:          typ.PkgPath(),
		IsPtr:              kd == reflect.Ptr,
		IsSlice:            kd == reflect.Slice,
		IsMap:              kd == reflect.Map,
		IsReferenceTypePtr: false,

		//IsPrimitive: typ.PkgPath() == "",
		//IsStruct:    false,
		IsInterface: kd == reflect.Interface,
		IsFunc:      kd == reflect.Func,
	}

	//var ref = typ
	if info.IsPtr {
		// 指针
		info.ReferenceType = typ.Elem()
		//checkPtrSupport(loc, info.Reference.Name(), info.Reference)
	} else if info.IsSlice {
		// 数组(切片)
		info.ReferenceType = typ.Elem()
		if info.ReferenceType.Kind() == reflect.Ptr {
			// 数组元素是指针
			info.IsReferenceTypePtr = true
			info.ReferenceType = info.ReferenceType.Elem()
		}
	} else if info.IsMap {
		// 检查 key 类型
		info.KeyType = typ.Key()
		kt := typ.Key()
		if kt.Kind() == reflect.Ptr {
			return nil, fmt.Errorf("map key [%s] must not a pointer at %s", kt.Name(), location)
		}
		_, ok := supportsPrimitiveTypes[kt.Kind()]
		if !ok {
			// map key 类型不支持
			return nil, fmt.Errorf("map key type [%s] is not support at %s", kt.Name(), location)
		}
		// 检查 value 类型
		info.ReferenceType = typ.Elem()
		if info.ReferenceType.Kind() == reflect.Slice {
			// map value 元素是 slice
			info.IsSlice = true
			info.ReferenceType = info.ReferenceType.Elem()
		}
		if info.ReferenceType.Kind() == reflect.Ptr {
			// map value 元素是指针
			info.IsReferenceTypePtr = true
			info.ReferenceType = info.ReferenceType.Elem()
		}

	}

	info.Name = info.ReferenceType.Name()
	info.Package = info.ReferenceType.PkgPath()
	info.IsReferenceStruct = info.ReferenceType.Kind() == reflect.Struct
	info.IsReferencePrimitive = info.Package == ""

	if !info.IsReferenceStruct && !info.IsSlice {
		_, ok := supportsPrimitiveTypes[info.ReferenceType.Kind()]
		if !ok {
			// 数据类型不支持
			return nil, fmt.Errorf("map value type [%s] is not support at %s", info.ReferenceType.Name(), location)
		}
	} else {
		// 结构体类型
		// 判断是否时间类型
		info.IsTime = IsTimeType(info.ReferenceType)
		// if info.IsTime {
		// 	// 时间类型认定为基础类型
		// 	info.IsReferenceStruct = false
		// }
	}
	// err := checkPtrSupport(loc, info.Name, info.Reference)

	return info, nil
}
